/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.Arsanesia.TempleRush.Models;

import java.util.Random;

import com.Arsanesia.TempleRush.Enumeration.BlockStateEnumeration;
import com.Arsanesia.TempleRush.Enumeration.GeenieEnumeration;
import com.Arsanesia.TempleRush.Enumeration.Notif;
import com.Arsanesia.TempleRush.Views.Screens.AbstractScreen;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.Array;

/**
 *
 * @author Chairul Ichsan
 */
public class Temple implements Updateable{

    public static final int STATE_READY = 0;
    public static final int STATE_FINISHED = 1;
    public static float LEFT_MARGIN;
    public static float BOTTOM_MARGIN;
    public static float TEMPLE_HEIGHTADJUSTMENT = 10;
    public static final int TOLERANCE_DISTANCE = 25;
    private static Temple self;
    
    private int state;
    private int activeFloor[];
    private int availableColor[];
    private Notif notiftype;
    private Block blocks[];
    private Array<Array<Sprite>> fireRegions;
    private Array<Sprite> maskRegions;
    private Array<Sprite> templeAnimationSprite;
    private Array<Sprite> temple;


    public static Temple getInstance(){
    	return self;
    }
    public Temple(TextureAtlas atlas) {
    	//create the block sprites
        temple= atlas.createSprites("temp");
        float blockwidth = temple.get(0).getRegionWidth();
        LEFT_MARGIN = AbstractScreen.VIEWPORT_DEFAULTWIDTH/2 - (blockwidth *2);
        BOTTOM_MARGIN = AbstractScreen.VIEWPORT_DEFAULTHEIGHT/8 - 6;
        Gdx.app.log("block width", blockwidth+"");
        //create the temple animation
        templeAnimationSprite = atlas.createSprites("TEMPBUILDED");
        
    	activeFloor = new int[4];

        for (int i = 0; i < activeFloor.length; i++) {
            activeFloor[i] = 0;
        }

        availableColor = new int[4];

        //cache all type of fire and mask
        fireRegions = new Array<Array<Sprite>>();
        maskRegions = new Array<Sprite>();
        
        //use different sprites for orbmode
        String firetype = (GameConfiguration.isOrbMode())? "ORB":"TEMP";
        for(int i=0; i<GeenieEnumeration.values().length;i++)
        {
        	fireRegions.add(atlas.createSprites(firetype+GeenieEnumeration.values()[i].name()));
        	maskRegions.add(atlas.createSprite("TEMPMASK"+GeenieEnumeration.values()[i].name()));
        }

        /*
         * Init all blocks for images and position
         */
        
        blocks = new Block[28];
        int currentImageIndex = 14;
        for (int i = 0; i < blocks.length; i++) {

            int j = i+1;
            boolean propagate = (j%4==2)||(j%4==3);
            boolean flip = (i<25)? (j%4==1)||(j%4==2): (j%2==1);
            		flip = (i==0 || i==1)? true: flip;//correction
            int imageIndex = (propagate && i<24)? currentImageIndex-1: currentImageIndex;
            blocks[i] = new Block(new TextureRegion(temple.get(0)));
            if (i > 25) { // Floor 8
                blocks[i].init((i / 4 + 2), (i % 4), new TextureRegion(temple.get(imageIndex)), templeAnimationSprite, flip);
            } else if (i > 23) { // Floor 7
                blocks[i].init((i / 4 + 1), (i % 4 + 2), new TextureRegion(temple.get(imageIndex)), templeAnimationSprite, flip);
                if(j%2 == 0) {currentImageIndex--;}
            } else { // Floor 1 - 6
                blocks[i].init((i / 4 + 1), (i % 4 + 1),new TextureRegion(temple.get(imageIndex)), templeAnimationSprite, flip);
                if(j%4 == 0) {currentImageIndex-=2;}
            }
            Gdx.app.log("debug currentImageIndex", imageIndex+"");
            
        }
    }

    public void addTempleToStage(Stage stage){
    	 for (int i = 0; i < blocks.length; i++) {
    		 blocks[i].addBlockToStage(stage);
    	 }
    }
    
    public int getState() {
        return state;
    }
    public Notif getNotifType(){
    	return this.notiftype;
    }
    public void setState(int state) {
        this.state = state;
    }
    public void setNotif(Notif n){
    	this.notiftype = n;
    }
    private void activateUpFloor(int column) {
        if (activeFloor[column] != -1) {
            int blockIndex = getBlockIndex(column);
            
            if (activeFloor[column] == 7) {
                //if(blocks[24].isBuilt() && blocks[25].isBuilt())
                	blockIndex = 26;
               // else 
                //	return; //if 7th floor not complete yet don't go to floor 8
            }
            
            blocks[blockIndex].setState(BlockStateEnumeration.READY);
            //resetColor();
            //availableColor[column] = blocks[blockIndex].getColor();

        } else {
        	
//            availableColor[column] = findActiveBlockColor();//availableColor[1];
//            Gdx.app.log("available", "found: "+GeenieEnumeration.values()[availableColor[column]].name());
        }
        resetColor();
    }
    
    private void resetColor(){
    	for(int column=0;column<=3;column++){
    		int color;
    		if(activeFloor[column]!=-1){
	    		int blockindex = getBlockIndex(column);
	    		color=  blocks[blockindex].getColor();
    		}
    		else {
	    		color=  findActiveBlockColor();
    		}
    		Gdx.app.log("reset","color "+column+" reseted: "+GeenieEnumeration.values()[color]);
    		availableColor[column] = color;
    	}
    }
    private int findActiveBlockColor(){
    	for(int column = 0; column<=3;column++)
    	{
	    		int blockindex = getBlockIndex(column);
	    		
	    		if(activeFloor[column] != -1){
	    			Gdx.app.log("block", "active floor: "+activeFloor[column]);
	    			Gdx.app.log("block", blockindex+"");
	    			return blocks[blockindex].getColor();
	    		}
    	}
    	return availableColor[1];
    }
    private void raiseFloor(int column) {
        switch (column) {
            case 0:
                if (activeFloor[0] < 5) {
                    ++activeFloor[0];
                } else {
                    if (activeFloor[1] == -1 && activeFloor[2] == -1 && activeFloor[3] == -1) {
                    	activeFloor[0] = -1;
                    	activeFloor[1] = 7;
                        activateUpFloor(1);
                    } else {
                        activeFloor[0] = -1;
                    }
//                	resetColor();
                }
                break;
            case 1:
                if (activeFloor[1] < 6) {
                    ++activeFloor[1];
                } else if (activeFloor[1] == 6) {
                    if (activeFloor[0] == -1 && activeFloor[2] == -1 && activeFloor[3] == -1) {
                        activeFloor[1] = 7;
                    } else {
                        activeFloor[1] = -1;
                    }
                    resetColor();
                } else {
                    state = STATE_FINISHED;
                }
                break;

            case 2:
                if (activeFloor[2] < 6) {
                    ++activeFloor[2];
                } else {
                    if (activeFloor[0] == -1 && activeFloor[1] == -1 && activeFloor[3] == -1) {
                    	//set column 2 to be inactive
                    	activeFloor[2] = -1;
                    	activeFloor[1] = 7;
                        activateUpFloor(1);
                    } else {
                        activeFloor[2] = -1;
                        
                    }
//                	resetColor();
                }
                break;
            case 3:
                if (activeFloor[3] < 5) {
                    ++activeFloor[3];
                } else {
                    if (activeFloor[0] == -1 && activeFloor[1] == -1 && activeFloor[2] == -1) {
                    	activeFloor[3] = -1;
                    	activeFloor[1] = 7;
                        activateUpFloor(1);
                    } else {
                        activeFloor[3] = -1;
                    }
//                	resetColor();
                }
                break;
        }
    }

    private int randomizeBlockColor(int level) {
        Random rand = new Random();
        int colorNumber;
        if(level< 6){
        	colorNumber = (level>1)? level:1;
        }
        else
        	colorNumber = 5;
//
//        if (level > 2) {
//            colorNumber = 4;
//        } else if (level > 1) {
//            colorNumber = 3;
//        } else if (level > 0) {
//            colorNumber = 2;
//        } else {
//        	//TODO change to 2??
//            //colorNumber = (level==0)? 0:1;
//        	colorNumber = 1;
//        }
        return rand.nextInt(colorNumber);
    }

    public void generateTemple(int level) {
        this.state = Temple.STATE_READY;

        /*
         * Generate Floor 1
         */
        for (int i = 0; i < 4; i++) {
            int blockColor = randomizeBlockColor(level);
            blocks[i].setAttributes(blockColor, BlockStateEnumeration.READY, fireRegions.get(blockColor), maskRegions.get(blockColor));
            availableColor[i] = blockColor;
        }

        /*
         * Generate Floor 2 - 8
         */
        for (int i = 4; i < 27; i++) {
            int blockColor = randomizeBlockColor(level);
            blocks[i].setAttributes(blockColor, BlockStateEnumeration.NON_ACCESS, fireRegions.get(blockColor), maskRegions.get(blockColor));

            /*
             * Floor 8
             */
            if (i == 26) {
                blocks[i + 1].setAttributes(blockColor, BlockStateEnumeration.NON_ACCESS, fireRegions.get(blockColor), maskRegions.get(blockColor));
            }
        }

        for (int i = 0; i < activeFloor.length; i++) {
            activeFloor[i] = 0;
        }
    }

    public void update() {
        for (int i = 0; i < blocks.length; i++) {
            if (blocks[i].needUpdate()) {
                blocks[i].update();
            }
        }
    }


    private int getBlockIndex(int column) {
        int blockIndex = activeFloor[column] * 4 + column;

        if (activeFloor[column] > 5) {
            --blockIndex;

            if (activeFloor[column] == 7) {
                blockIndex -= 2;
            }
        }

        return blockIndex;
    }

    public Notif checkTouch(float x,float y, GeenieEnumeration color) {
        float xRelative = x - LEFT_MARGIN + TOLERANCE_DISTANCE; // Touch position relative to temple position
        int column = (int) ((xRelative) / Block.WIDTH + 1); // Column position of touch coordinate

        if (column > 0 && column < 5) { // Process only 4 column of temple block
            /*
             * Decrement column value to match iteration standard
             */
            --column;

            /*
             * Hit against already finished column is counted as miss
             */
            if (activeFloor[column] == -1) {
                return Notif.MISS;
            }

            /*
             * Retrieve the target temple block
             */
            int blockIndex = getBlockIndex(column);

            /*
             * Check whether the touch position hit target block
             */
            Notif hit = blocks[blockIndex].checkTouch(x,y,color);

            /*
             * If it hits, activate the upper floor
             */
            if (hit != Notif.MISS) {
                /*
                 * If hit happens on floor 8, then two blocks will be finished
                 */
            	Gdx.app.log("templeinstance", "set state");
                if (activeFloor[column] == 7) {
                    blocks[27].setState(BlockStateEnumeration.ANIMATE);
                }

                /*
                 * Increase the value of active floor
                 */
            	Gdx.app.log("templeinstance", "raise floor");
                raiseFloor(column);

                /*
                 * Activate the upper floor
                 */
                Gdx.app.log("templeinstance", "activate up floor");
                activateUpFloor(column);
            }

            return hit;
        } else {
            return Notif.MISS;
        }
    }

    public void kingGeenieEffect() {
        for (int column = 0; column < 4; column++) {
            if (activeFloor[column] != -1) {
                /*
                 * Retrieve the target temple block
                 */
                int blockIndex = getBlockIndex(column);

                /*
                 * Build the block
                 */
                blocks[blockIndex].setState(BlockStateEnumeration.ANIMATE);

                /*
                 * Increase the value of active floor
                 */
                raiseFloor(column);

                /*
                 * Activate the upper floor
                 */
                activateUpFloor(column);
            }
        }
    }

    public void blackGeenieEffect() {
//    	for(int column=0;column<4;column++){
//    		if(activeFloor[column]!=-1){
//	    		int blockindex = getBlockIndex(column);
//	    		blocks[blockindex].setState(BlockStateEnumeration.NON_ACCESS);
//	    		
//    		}
//    	}
        /*
         * Reset Floor 1
         */
        for (int i = 0; i < 4; i++) {
            blocks[i].setState(BlockStateEnumeration.READY);
            availableColor[i] = blocks[i].getColor();
        }

        /*
         * Reset Floor 2 - 8
         */
        for (int i = 4; i < blocks.length; i++) {
            blocks[i].setState(BlockStateEnumeration.NON_ACCESS);
        }

        /*
         * Reset Active Floor
         */
        for (int i = 0; i < activeFloor.length; i++) {
            activeFloor[i] = 0;
        }
    }

    public int[] getAvailableColor() {
        return availableColor;
    }
}
